using System;
using System.Collections.Generic;
using System.Text;

namespace SmartMathLibrary
{
    /// <summary>
    /// The aggregate class provides usual operations to work with mathematical aggregates.
    /// </summary>
    [Serializable]
    public class Aggregate : IAggregate
    {
        /// <summary>
        /// The aggregate number list.
        /// </summary>
        private List<double> data;

        /// <summary>
        /// Initializes a new instance of the <see cref="Aggregate"/> class.
        /// </summary>
        public Aggregate()
        {
            this.data = new List<double>();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Aggregate"/> class.
        /// </summary>
        /// <param name="list">The list of numbers for the aggregate.</param>
        public Aggregate(double[] list)
        {
            if (list == (double[]) ((object) null))
            {
                throw new ArgumentNullException("list");
            }

            this.data = new List<double>();

            for (int i = 0; i < list.Length; i++)
            {
                this.data.Add(list[i]);
            }

            this.Sort();
        }

        /// <summary>
        /// Gets or sets the data of the aggregate.
        /// </summary>
        /// <value>The data of the aggregate.</value>
        public List<double> Data
        {
            get { return this.data; }
            set { data = value; }
        }

        /// <summary>
        /// Gets the number of items of the aggregate.
        /// </summary>
        /// <value>The number of items of the aggregate.</value>
        public int Count
        {
            get { return this.data.Count; }
        }

        /// <summary>
        /// Determines whether the [aggregate is empty].
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if the [aggregate is empty]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsEmptyAggregate()
        {
            return this.data.Count == 0;
        }

        /// <summary>
        /// Converts the data of the aggregate to an array of numbers.
        /// </summary>
        /// <returns>The converted array of numbers.</returns>
        public double[] ToArray()
        {
            double[] tempuri = new double[this.data.Count];

            this.data.CopyTo(tempuri);

            return tempuri;
        }

        /// <summary>
        /// Sorts the list of aggregate numbers.
        /// </summary>
        public void Sort()
        {
            this.data.Sort();
        }

        /// <summary>
        /// Adds a new element to the aggregate.
        /// </summary>
        /// <param name="item">The item to add.</param>
        public void AddElement(double item)
        {
            this.data.Add(item);
        }

        /// <summary>
        /// Removes the element at the specified position.
        /// </summary>
        /// <param name="index">The specified position of the item to delete.</param>
        public void RemoveElement(int index)
        {
            this.data.RemoveAt(index);
        }

        /// <summary>
        /// Removes the element with the specified value from the aggregate.
        /// </summary>
        /// <param name="item">The specified value of the item to remove.</param>
        public void RemoveElement(double item)
        {
            this.data.Remove(item);
        }

        /// <summary>
        /// Removes all elements from the aggregate.
        /// </summary>
        public void RemoveAllElements()
        {
            this.data.Clear();
        }

        /// <summary>
        /// Determines whether the current aggregate instance [contains] [the specified value].
        /// </summary>
        /// <param name="value">The value to check.</param>
        /// <returns>
        /// 	<c>true</c> if the current aggregate instance [contains] [the specified value]; otherwise, <c>false</c>.
        /// </returns>
        public bool Contains(double value)
        {
            return this.data.Contains(value);
        }

        /// <summary>
        /// Determines whether the specified aggregate [is a subset of] [the current aggregate instance].
        /// </summary>
        /// <param name="compare">The aggregate to compare.</param>
        /// <returns>
        /// 	<c>true</c> if the current aggregate instance [is a subset of] [the specified aggregate to compare]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsSubsetOf(Aggregate compare)
        {
            if (compare == (Aggregate) null)
            {
                throw new ArgumentNullException("compare");
            }

            foreach (double d in this.data)
            {
                if (!compare.Contains(d))
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Creates the intersection of the current aggregate instance and another 
        /// specified aggregate.
        /// </summary>
        /// <param name="intersection">The aggregate to create the intersection.</param>
        /// <returns>The created intersection.</returns>
        public Aggregate Intersection(Aggregate intersection)
        {
            if (intersection == (Aggregate) null)
            {
                throw new ArgumentNullException("intersection");
            }

            Aggregate result = new Aggregate();

            foreach (double d in this.data)
            {
                if (this.data.Contains(d) && intersection.Data.Contains(d))
                {
                    result.AddElement(d);
                }
            }

            result.Sort();

            return result;
        }

        /// <summary>
        /// Creates the differences between the current aggregate and the 
        /// specified one.
        /// </summary>
        /// <param name="difference">The aggregate to difference with.</param>
        /// <returns>The created aggregate.</returns>
        public Aggregate Difference(Aggregate difference)
        {
            if (difference == (Aggregate) null)
            {
                throw new ArgumentNullException("difference");
            }

            Aggregate result = new Aggregate();

            foreach (double d in this.data)
            {
                if (!difference.Data.Contains(d))
                {
                    result.AddElement(d);
                }
            }

            result.Sort();

            return result;
        }

        /// <summary>
        /// Creates the union of the current aggregate instance and another 
        /// specified aggregate.
        /// </summary>
        /// <param name="union">The aggregate to create the union.</param>
        /// <returns>The created union.</returns>
        public Aggregate Union(Aggregate union)
        {
            if (union == (Aggregate) null)
            {
                throw new ArgumentNullException("union");
            }

            Aggregate result = new Aggregate(this.ToArray());

            for (int i = 0; i < union.Count; i++)
            {
                if (!this.Contains(union.Data[i]))
                {
                    result.AddElement(union.Data[i]);
                }
            }

            result.Sort();

            return result;
        }

        /// <summary>
        /// Creates the cross product of the current aggregate instance and another
        /// specified aggregate.
        /// </summary>
        /// <param name="aggregate">The other aggregate for creating the cross product.</param>
        /// <returns>The created cross product.</returns>
        public Point2DCollection CrossProduct(Aggregate aggregate)
        {
            if (aggregate == (Aggregate) null)
            {
                throw new ArgumentNullException("aggregate");
            }

            Point2DCollection result = new Point2DCollection();

            foreach (double a in this.data)
            {
                foreach (double b in aggregate.Data)
                {
                    result.Add(new Point2D(a, b));
                }
            }

            return result;
        }

        /// <summary>
        /// Compares the current instance of an aggregate to another one.
        /// </summary>
        /// <param name="compare">The aggregate to compare.</param>
        /// <returns>True, if the current aggregate data is even to the specified
        /// aggregate data otherwise, false.</returns>
        public bool CompareTo(Aggregate compare)
        {
            if (compare == (Aggregate) null)
            {
                throw new ArgumentNullException("compare");
            }

            foreach (double d in this.Data)
            {
                if (compare.Data.Contains(d))
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        /// <param name="a">The first aggregate.</param>
        /// <param name="b">The second aggregate.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator ==(Aggregate a, Aggregate b)
        {
            if (System.Object.ReferenceEquals(a, b))
            {
                return true;
            }

            if (((object) a == null) || ((object) b == null))
            {
                return false;
            }

            return a.CompareTo(b);
        }

        /// <summary>
        /// Implements the operator !=.
        /// </summary>
        /// <param name="a">The first aggregate.</param>
        /// <param name="b">The second aggregate.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator !=(Aggregate a, Aggregate b)
        {
            return !(a == b);
        }

        /// <summary>
        /// Serves as a hash function for a particular type.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"/>.
        /// </returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        /// <exception cref="T:System.NullReferenceException">The <paramref name="obj"/> parameter is null.</exception>
        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }

        /// <summary>
        /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </returns>
        public override string ToString()
        {
            String result = "{";

            foreach (double d in this.data)
            {
                result += d.ToString() + "; ";
            }

            if (result.Length > 2)
            {
                result = result.Substring(0, result.Length - 2);
            }

            return result + "}";
        }
    }
}